//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Formats.Biff.Tokens;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
#if DEBUG
    public
#else
        internal
#endif
        sealed class FormulaRecord : CellRecord, IWorksheetLevelRecord, IFormulaRecord
    {
#if DEBUG
        public IEnumerable<object> Tokens
        {
            get
            {
                foreach (FormulaToken o in _parsedFormula.GetTokens())
                {
                    yield return o;
                }
            }
        }
#endif
        internal ArrayRecord ArrayInfo { get; set; }
        private readonly FormulaValue _formulaValue = new FormulaValue();
        //private ulong _formulaResultCode;
        private CellParsedFormula _parsedFormula;
        private FormulaOptionFlag grbit;
        internal uint chn = 4261675013;

        internal FormulaRecord(Cell cell, BiffWriterWorkbookContext context) : base(cell, context)
        {
            _parsedFormula = CreateFormulaValue(cell.Expression, context, cell);
            _formulaValue = new FormulaValue(cell.Value);
            if (cell.Expression is SharedExpressionReference)
                grbit = FormulaOptionFlag.fShrFmla;
        }

        private static CellParsedFormula CreateFormulaValue(Expression expression, BiffWriterWorkbookContext context, Cell cell)
        {
            return ParsedFormulaBase.GetFormulaValue<CellParsedFormula>(context, expression, cell);
        }

        public FormulaRecord()
        {
        }

        #region IFormulaRecord Members

        //public object FormulaResult { get; set; }

        #endregion

        private object getActualFormulaResult()
        {
            return _formulaValue.GetValueFromCode();
        }

        internal override void ApplyCore(BiffReaderWorksheetContext context)
        {
            context.WorkbookContext.GetActions(ActionType.Formulas).Add(delegate
                                                                            {
                                                                                //Debug.WriteLine("processing " + ToString());
                                                                                Cell target =
                                                                                    context.Worksheet.EnsurePromotedCell(Row, Column);
                                                                                Expression exp;
                                                                                if (ArrayInfo != null)
                                                                                {
                                                                                    Range range =
                                                                                        context.Worksheet.GetRange(
                                                                                            ArrayInfo.Address);
                                                                                    exp =
                                                                                        ArrayInfo.Formula.GetExpression(
                                                                                            context, target);
                                                                                    var af = new ArrayFormula(exp, range);
                                                                                    context.ArrayFormulas.Add(
                                                                                        target.Address, af);
                                                                                    foreach (
                                                                                        Cell cell in range.GetAllCells()
                                                                                        )
                                                                                    {
                                                                                        cell.SetExpression(
                                                                                            new ArrayFormulaReference(af));
                                                                                    }
                                                                                    target.Value =
                                                                                        getActualFormulaResult();
                                                                                    return;
                                                                                }

                                                                                  exp =
                                                                                        _parsedFormula.GetExpression(
                                                                                            context, target);
                                                                                    target.Value = 0;
                                                                                    target.Value = getActualFormulaResult();
                                                                                if (exp is ConstantExpression)
                                                                                {
                                                                                    ConstantExpression ce = (ConstantExpression) exp;
                                                                                    target.Value = ce.Value;
                                                                                }
                                                                                else   target.setExpressionValue(exp);

                                                                               
                                                                            });
        }

        protected override void WriteValue(BinaryWriter writer)
        {
            _formulaValue.Write(writer);
            writer.Write((ushort) grbit);
            writer.Write(chn);
            _parsedFormula.Write(writer);
        }

        protected override void ReadValue(BinaryReader reader, int length)
        {
#if DEBUG
            long oldPosition = reader.BaseStream.Position;
#endif
            _formulaValue.Read(reader);
            grbit = (FormulaOptionFlag) reader.ReadUInt16();
            chn = reader.ReadUInt32();

            //ushort formulaLength = reader.ReadUInt16();
            _parsedFormula = new CellParsedFormula();
            _parsedFormula.Read(new FormulaReadContext(reader, FormulaReadContextType.Default));
#if DEBUG
            Contract.Assert(length == reader.BaseStream.Position - oldPosition);
#endif
        }

        public object FormulaResult
        {
            get { return _formulaValue.Value; }
            set { _formulaValue.Value = value; }
        }

        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.Formula; }
        }
    }
}